package com.javabasic.algorithm.lru;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @Author xiongmin
 * @Description //TODO
 * @Date 2020/4/23 11:14
 * @Version 1.0
 **/
public class MyLruCache<K, V> {

    /**
     * 缓存得最大容量
     */
    private final int maxCapacity;

    private ConcurrentHashMap<K, V> cacheMap;
    private ConcurrentLinkedQueue<K> keys;

    /**
     * 读写锁
     */
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    private Lock writeLock = readWriteLock.writeLock();

    private Lock readLock = readWriteLock.readLock();


    public MyLruCache(int maxCapacity) {
        if (maxCapacity < 0) {
            throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
        }
        this.maxCapacity = maxCapacity;
        cacheMap = new ConcurrentHashMap<>(maxCapacity);
        keys = new ConcurrentLinkedQueue<>();
    }


    public V put(K key, V value) {
        // TODO 加写锁
        writeLock.lock();
        try {
            // TODO: 1. key 是否存在当前缓存中
            if (cacheMap.containsKey(key)) {
                moveToTailOfQueue(key);
                cacheMap.put(key, value);
                return value;
            }

            // TODO: 2. 是否超出缓存容量，超出得话就移除队列头部的key元素以及对应的缓存
            if (keys.size() >= maxCapacity) {
                System.out.println("maxCapacity of cache reached");
                removeOldestKey();
            }

            // TODO: 3. key不存在于当前缓存中，将key压入压入队尾并且缓存key及其对应的缓存
            keys.add(key);
            cacheMap.put(key, value);
            return value;
        } finally {
            writeLock.unlock();
        }
    }

    public V get(K key) {
        // TODO 加读锁
        readLock.lock();
        try {
            // TODO 1. 判断keys是否存在与缓存中
            if (cacheMap.containsKey(key)) {

                // TODO 2. 更新key的访问位置
                moveToTailOfQueue(key);
                return cacheMap.get(key);
            } else {
                // TODO 3. 不存在缓存中就返回null
                return null;
            }
        } finally {
            readLock.unlock();
        }
    }

    public V remove(K key) {
        writeLock.lock();
        try {
            // TODO 1. 判断keys是否存在与缓存中
            if (cacheMap.containsKey(key)) {
                // TODO 2. 移除Keys队列中的key以及缓存
                keys.remove(key);
                return cacheMap.remove(key);
            } else {
                // TODO 3. 不存在缓存中就返回null
                return null;
            }
        } finally {
            writeLock.unlock();
        }
    }

    private void removeOldestKey() {
        K oldestKey = keys.poll();
        if (oldestKey != null) {
            cacheMap.remove(oldestKey);
        }
    }

    private void moveToTailOfQueue(K key) {
        keys.remove(key);
        keys.add(key);
    }


    public int size() {
        return cacheMap.size();
//        return keys.size();
    }


}
